package instruction

import (
	"gitee.com/microwww/jvm-go/jvm/rtda"
	"gitee.com/microwww/jvm-go/jvm/tool"
)

type GOTO struct {
	offsetInstruct
}

func (f *GOTO) Execute(fm *rtda.MethodFrame) {
	f.Branch(fm)
}

type TableSwitch struct {
	defaultOffset int32
	low           int32
	height        int32
	jumpOffsets   []int32
}

func (f *TableSwitch) FetchOperands(bf *tool.Buffer) {
	bf.SkipPadding()
	f.defaultOffset = bf.ReadInt32()
	f.low = bf.ReadInt32()
	f.height = bf.ReadInt32()
	cnt := f.height - f.low + 1
	f.jumpOffsets = bf.ReadInt32s(int(cnt))
}

func (f *TableSwitch) Execute(fm *rtda.MethodFrame) {
	stack := fm.OperationStack()
	v := stack.PopInt()
	off := f.defaultOffset
	if v >= f.low && v <= f.height {
		off = f.jumpOffsets[v-f.low]
	}
	Branch(fm, int(off))
}

type LookupSwitch struct {
	defaultOffset int32
	npairs        int32
	matchOffsets  []int32
}

func (f *LookupSwitch) FetchOperands(bf *tool.Buffer) {
	bf.SkipPadding()
	f.defaultOffset = bf.ReadInt32()
	f.npairs = bf.ReadInt32()
	cnt := f.npairs * 2
	f.matchOffsets = bf.ReadInt32s(int(cnt))
}

func (f *LookupSwitch) Execute(fm *rtda.MethodFrame) {
	off := f.defaultOffset
	v := fm.OperationStack().PopInt()
	cnt := f.npairs * 2
	for i := int32(0); i < cnt; i += 2 {
		if f.matchOffsets[i] == v {
			off = f.matchOffsets[i+1]
			break
		}
	}
	Branch(fm, int(off))
}

type GOTOW struct {
	GOTO
}

func (f *GOTOW) FetchOperands(bf *tool.Buffer) {
	v := bf.ReadInt32() // 可以是 `负数`
	f.Offset = int(v)
}

type Return struct {
	Noop
}

func (f *Return) Execute(fr *rtda.MethodFrame) {
	fr.Thread().PopMethodFrame()
}

type IReturn struct {
	Noop
}

func (f *IReturn) Execute(fr *rtda.MethodFrame) {
	thread := fr.Thread()
	last := thread.PopMethodFrame()
	next := thread.CurrentMethodFrame()
	val := last.OperationStack().PopInt()
	next.OperationStack().PushInt(val)
}

type LReturn struct {
	Noop
}

func (f *LReturn) Execute(fr *rtda.MethodFrame) {
	thread := fr.Thread()
	last := thread.PopMethodFrame()
	next := thread.CurrentMethodFrame()

	val := last.OperationStack().PopLong()
	next.OperationStack().PushLong(val)
}

type FReturn struct {
	Noop
}

func (f *FReturn) Execute(fr *rtda.MethodFrame) {
	thread := fr.Thread()
	last := thread.PopMethodFrame()
	next := thread.CurrentMethodFrame()
	val := last.OperationStack().PopFloat()
	next.OperationStack().PushFloat(val)
}

type DReturn struct {
	Noop
}

func (f *DReturn) Execute(fr *rtda.MethodFrame) {
	thread := fr.Thread()
	last := thread.PopMethodFrame()
	next := thread.CurrentMethodFrame()
	val := last.OperationStack().PopDouble()
	next.OperationStack().PushDouble(val)
}

type AReturn struct {
	Noop
}

func (f *AReturn) Execute(fr *rtda.MethodFrame) {
	thread := fr.Thread()
	last := thread.PopMethodFrame()
	next := thread.CurrentMethodFrame()
	ref := last.OperationStack().PopRef()
	next.OperationStack().PushRef(ref)
}
